package fair.aop;

import com.alibaba.fastjson.JSON;
import fair.pojo.Record;
import fair.service.IRecordService;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.Reference;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

/**
 * @author: Jingtao_Wu
 * @DATE: 2021-03-03 09:58
 **/
@Aspect
@Component
@Slf4j
public class LogAop {
    private Class clazz;

    private Method serviceMethod;

    Record record = new Record();

    @Reference(version = "${dubbo.service.core.version}", group = "${dubbo.service.core.group}", timeout = 100000)
    private IRecordService recordService;

    @Before("execution(* fair.controller.*.*(..))")
    public void doBefore(JoinPoint joinPoint) throws NoSuchMethodException {
        //具体要访问的类
        clazz = joinPoint.getTarget().getClass();
        //获取访问的方法名称
        String methodName = joinPoint.getSignature().getName();
        record.setServiceMethod(methodName);
    }

    /*
     * 后置通知
     * @param joinPoint 切点
     */

    @AfterReturning(value = "execution(* fair.controller.*.*(..))", returning = "result")
    public void doAfter(JoinPoint joinPoint, Object result){
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        //从获取RequestAttributes中获取HttpServletRequest的信息
        HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
        record.setResult(result.toString());
        getIpAndUrl(request);
        getParams(joinPoint, request);
        getHeaders(request);
//        getResult(joinPoint);
        getUsername();

        recordService.save(record);
    }

    private void getUsername() {
        //获取请求用户名
        record.setUserName(SecurityContextHolder.getContext().getAuthentication().getName());
    }

    private void getHeaders(HttpServletRequest request) {
        //获取请求头字段
        Enumeration<String> headerNames = request.getHeaderNames();
        Map<String, Object> headers = new HashMap<>(10);
        while (headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();
            String headerValue = request.getHeader(headerName);
            headers.put(headerName, headerValue);
        }
        record.setHeader(JSON.toJSONString(headers));
    }

    private void getIpAndUrl(HttpServletRequest request) {
        //获取请求ip和请求路径
        String ip = request.getRemoteAddr();
        String localIp = "127.0.0.1";
        ip = "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
        if (localIp.equals(ip)){
            String nginxIp = request.getHeader("x-forwarded-for");
            record.setNginxIp(nginxIp);
        }
        record.setRequestIp(ip);
        record.setPath(request.getRequestURI());
    }

    private void getParams(JoinPoint joinPoint, HttpServletRequest request) {
        //根据请求方法获取请求参数
        Object[] args = joinPoint.getArgs();
        String method = request.getMethod();
        String post = "POST";
        String put = "PUT";
        String get = "GET";
        record.setRequestMethod(method);
        if (post.equals(method) || put.equals(method)){
            Object object = args[0];
            Map map = getKeyAndValue(object);
            record.setBody(JSON.toJSONString(map));
        }else if(get.equals(method)){
            Map<String, String[]> parameterMap = request.getParameterMap();
            record.setQuery(JSON.toJSONString(parameterMap));
        }
    }

    public static Map<String, Object> getKeyAndValue(Object obj) {
        Map<String, Object> map = new HashMap<>(10);
        // 得到类对象
        Class userCla = (Class) obj.getClass();
        /* 得到类中的所有属性集合 */
        Field[] fs = userCla.getDeclaredFields();
        for (int i = 0; i < fs.length; i++) {
            Field f = fs[i];
            f.setAccessible(true);
            Object val = new Object();
            try {
                val = f.get(obj);
                // 得到此属性的值
                map.put(f.getName(), val);
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return map;
    }
}
